/**
 * 
 */
package sap.provider;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import sap.Connection;

import com.sap.mw.jco.JCO;

import data.MessageType;
import data.PSPHierarchy;
import data.ProjectDefinition;
import data.ProjectList;
import data.ReturnValue;
import exceptions.ReturnException;

/**
 * @author sebastian
 * 
 */
final class PspSapHierarchy {
	public static PSPHierarchy formStruct(JCO.Record struct) {
		PSPHierarchy result = new PSPHierarchy();
		
		result.wbs_element = struct.getString("WBS_ELEMENT");
		result.up = struct.getString("UP");
		result.down = struct.getString("DOWN");
		result.left = struct.getString("LEFT");
		result.right = struct.getString("RIGHT");
		
		return result;
	}
	
	public static void setLinks(PSPHierarchy h, Map<String, PSPHierarchy> map) {
		h.up_link = map.get(h.up);
		h.down_link = map.get(h.down);
		h.left_link = map.get(h.left);
		h.right_link = map.get(h.right);
	}
}

/**
 * @author sebastian
 * 
 */
public final class Project implements Methods {
	public static boolean exists(String definition) {
		// TODO find thrown exceptions
		Connection con = Connection.getInstance();

		JCO.Function function = con.getFunction("BAPI_PROJECTDEF_EXISTENCECHECK");

		// fill import parameter
		function.getImportParameterList().setValue(definition,
				"CURRENTEXTERNALPROJE");

		con.execute(function);

		ReturnValue result = Return.getFromStructure(function
				.getExportParameterList().getStructure("RETURN"));

		// special error, project not exists
		if (result.type == MessageType.ERROR && result.id.equals("CJ")
				&& result.number == 11)
			return false;

		// other errors
		Return.testReturnValue(result);

		return true;
	}

	public static ProjectDefinition get(String definition)
			throws JCO.ConversionException, JCO.AbapException, JCO.Exception,
			ReturnException {
		Connection con = Connection.getInstance();

		JCO.Function function = con.getFunction("BAPI_PROJECTDEF_GETDETAIL");

		// fill import parameter
		function.getImportParameterList().setValue(definition,
				"CURRENTEXTERNALPROJE");

		con.execute(function);

		JCO.ParameterList export = function.getExportParameterList();

		Return.testReturnValue(Return.getFromStructure(export
				.getStructure("RETURN")));

		return projectFromClass(export.getStructure("PROJECT_DEFINITION_STRU"));
	}
	
	public static ProjectList[] getList(String regex) {
		Connection con = Connection.getInstance();
		
		JCO.Function function = con.getFunction("BAPI_PROJECTDEF_GETLIST");
		
		// fill import parameter
		JCO.Table t = function.getTableParameterList()
			.getTable("PROJECT_DEFINITION_RANGE");
		t.appendRow();
		t.setValue('I', "SIGN");
		t.setValue("CP", "OPTION");
		t.setValue((regex.isEmpty() ? "*" : regex), "LOW");
		
		con.execute(function);
		
		Return.testReturnValue(Return.getFromStructure(function.getExportParameterList()
				.getStructure("RETURN")));
		
		t = function.getTableParameterList()
			.getTable("PROJECT_DEFINITION_LIST");
		int num = t.getNumRows();
		ProjectList[] result = new ProjectList[num];
		for (int i=0; i<num; i++) {
			result[i] = new ProjectList();
			result[i].project_definition = t.getString(0);
			result[i].description = t.getString(1);
			
			t.nextRow();
		}
		
		return result;
	}

	public static void create(ProjectDefinition project) {
		// TODO find exceptions
		Connection con = Connection.getInstance();

		JCO.Function function = con.getFunction("BAPI_PROJECTDEF_CREATE");

		// fill import parameter
		structFromClass(project, function.getImportParameterList().getStructure(
					"PROJECT_DEFINITION_STRU"));

		con.execute(function);
		
		ReturnValue r = Return.getFromStructure(function.
				getExportParameterList().getStructure("RETURN"));
		ReturnValue[] detail = Return.getFromTable(function.
				getTableParameterList().getTable("E_MESSAGE_TABLE"));
		Return.testReturnValue(r, detail);
	}
	
	public static void maintain(ProjectDefinition project) {
		// TODO find exceptions
		Connection con = Connection.getInstance();
		
		JCO.Function function = con.getFunction("BAPI_PROJECT_MAINTAIN");
		
		// fill import parameter
		structFromClass(project, function.getImportParameterList()
				.getStructure("I_PROJECT_DEFINITION"));
		JCO.ParameterList tables = function.getTableParameterList();
		JCO.Table t = tables.getTable("I_METHOD_PROJECT");
		t.appendRow();
		t.setValue("ProjectDefinition", "OBJECTTYPE");
		t.setValue(UPDATE_METHOD, "METHOD");
		t.setValue(project.project_definition, "OBJECTKEY");
		t.setValue(1, "REFNUMBER");
		t.appendRow();
		t.setValue("Save", "METHOD");
		JCO.ParameterList im = function.getImportParameterList();
		structFromClass(project, im.getStructure("I_PROJECT_DEFINITION"));
		Connection.fillX(im.getStructure("I_PROJECT_DEFINITION_UPD"), 1);
		
		con.execute(function);

		ReturnValue r = Return.getFromStructure(function.
				getExportParameterList().getStructure("RETURN"));
		ReturnValue[] detail = Return.getFromTable(function.
				getTableParameterList().getTable("E_MESSAGE_TABLE"));
		Return.testReturnValue(r, detail);
		
		/*JCO.Function function = con.getFunction("BAPI_PROJECTDEF_UPDATE");
		
		// fill import parameter
		JCO.ParameterList im = function.getImportParameterList();
		im.setValue(project.description, "CURRENTEXTERNALPROJE");
		structFromClass(project, im.getStructure("PROJECT_DEFINITION_STRU"));
		Connection.fillX(im.getStructure("PROJECT_DEFINITION_UP"), 1);
		
		con.execute(function);
		
		ReturnValue r = Return.getFromStructure(function.
				getExportParameterList().getStructure("RETURN"));
		ReturnValue[] detail = Return.getFromTable(function.
				getTableParameterList().getTable("E_MESSAGE_TABLE"));
		Return.testReturnValue(r, detail);*/
	}
	
	public static void delete(String definition) {
		Connection con = Connection.getInstance();
		JCO.Function function;

		// initialization
		function = con.getFunction("BAPI_PS_INITIALIZATION");
		con.execute(function);

		// delete project
		function = con.getFunction("BAPI_BUS2001_DELETE");
		function.getImportParameterList().setValue(definition,
				"I_PROJECT_DEFINITION");
		con.execute(function);
		ReturnValue[] detail = Return.getFromTable(function.
				getTableParameterList().getTable("ET_RETURN"));
		Return.testReturnValue(null, detail);

		// commit work
		function = con.getFunction("BAPI_PS_PRECOMMIT");
		con.execute(function);
		detail = Return.getFromTable(function.
				getTableParameterList().getTable("ET_RETURN"));
		Return.testReturnValue(null, detail);

		function = con.getFunction("BAPI_TRANSACTION_COMMIT");
		con.execute(function);
		Return.testReturnValue(Return.getFromStructure(function.getExportParameterList()
				.getStructure("RETURN")));
	}
	
	public static List<PSPHierarchy> getHierarchy(String definition) {
		// TODO find exceptions
		Connection con = Connection.getInstance();
		
		JCO.Function function = con.getFunction("BAPI_PROJECT_GETINFO");
		
		// fill import parameter
		function.getImportParameterList().setValue(definition, "PROJECT_DEFINITION");
		
		con.execute(function);
		
		ReturnValue r = Return.getFromStructure(function.
				getExportParameterList().getStructure("RETURN"));
		ReturnValue[] detail = Return.getFromTable(function.
				getTableParameterList().getTable("E_MESSAGE_TABLE"));
		Return.testReturnValue(r, detail);
		
		JCO.Table t = function.getTableParameterList().getTable("E_WBS_HIERARCHIE_TABLE");
		int num = t.getNumRows();
		HashMap<String, PSPHierarchy> map = new HashMap<String, PSPHierarchy>(num);
		for (int i=0; i<num; i++) {
			PSPHierarchy h = PspSapHierarchy.formStruct(t);
			map.put(h.wbs_element, h);
			
			t.nextRow();
		}
		Iterator<String> keys = map.keySet().iterator();
		PSPHierarchy next = null;
		for (; keys.hasNext(); ) {
			PSPHierarchy h = map.get(keys.next());
			PspSapHierarchy.setLinks(h, map);
			if (h.up_link == null && h.left_link == null)
				next = h; 
		}
		
		ArrayList<PSPHierarchy> result = new ArrayList<PSPHierarchy>();
		while (next != null) {
			result.add(next);
			next = setChildren(next);
		}
		
		return result;
	}

	private static void structFromClass(ProjectDefinition project, JCO.Structure struct) {
		struct.setValue(project.project_definition, 0);
		struct.setValue(project.description, 1);
		struct.setValue(project.mask_id, 2);
		struct.setValue(project.responsible_no, 3);
		struct.setValue(project.applicant_no, 4);
		struct.setValue(project.comp_code, 5);
		struct.setValue(project.bus_area, 6);
		struct.setValue(project.controlling_area, 7);
		struct.setValue(project.profit_ctr, 8);
		struct.setValue(project.project_currency, 9);
		struct.setValue(project.project_currency_iso, 10);
		struct.setValue(project.network_assignment, 11);
		struct.setValue(project.start, 12);
		struct.setValue(project.finish, 13);
		struct.setValue(project.plant, 14);
		struct.setValue(project.calendar, 15);
		struct.setValue(project.plan_basic, 16);
		struct.setValue(project.plan_fcst, 17);
		struct.setValue(project.time_unit, 18);
		struct.setValue(project.time_unit_iso, 19);
		struct.setValue(project.network_profile, 20);
		struct.setValue(project.project_profile, 21);
		struct.setValue(project.budget_profile, 22);
		struct.setValue(project.project_stock, 23);
		struct.setValue(project.objectclass, 24);
		struct.setValue(project.statistical, 25);
		struct.setValue(project.taxjurcode, 26);
		struct.setValue(project.int_profile, 27);
		struct.setValue(project.wbs_sched_profile, 28);
		struct.setValue(project.csh_bdgt_profile, 29);
		struct.setValue(project.plan_profile, 30);
		struct.setValue(project.joint_venture, 31);
		struct.setValue(project.recovery_ind, 32);
		struct.setValue(project.equity_type, 33);
		struct.setValue(project.jv_object_type, 34);
		struct.setValue(project.jv_jib_class, 35);
		struct.setValue(project.jv_jib_sub_class, 36);
		struct.setValue(project.objectclass_ext, 37);
		struct.setValue(project.func_area, 38);
		struct.setValue(project.func_area_long, 39);
	}

	private static ProjectDefinition projectFromClass(JCO.Structure struct)
			throws JCO.ConversionException {
		ProjectDefinition result = new ProjectDefinition();

		result.project_definition = struct.getString(0);
		result.description = struct.getString(1);
		result.mask_id = struct.getString(2);
		result.responsible_no = struct.getString(3);
		result.applicant_no = struct.getString(4);
		result.comp_code = struct.getString(5);
		result.bus_area = struct.getString(6);
		result.controlling_area = struct.getString(7);
		result.profit_ctr = struct.getString(8);
		result.project_currency = struct.getString(9);
		result.project_currency_iso = struct.getString(10);
		result.network_assignment = struct.getString(11);
		result.start = struct.getString(12);
		result.finish = struct.getString(13);
		result.plant = struct.getString(14);
		result.calendar = struct.getString(15);
		result.plan_basic = struct.getString(16);
		result.plan_fcst = struct.getString(17);
		result.time_unit = struct.getString(18);
		result.time_unit_iso = struct.getString(19);
		result.network_profile = struct.getString(20);
		result.project_profile = struct.getString(21);
		result.budget_profile = struct.getString(22);
		result.project_stock = struct.getString(23);
		result.objectclass = struct.getString(24);
		result.statistical = struct.getString(25);
		result.taxjurcode = struct.getString(26);
		result.int_profile = struct.getString(27);
		result.wbs_sched_profile = struct.getString(28);
		result.csh_bdgt_profile = struct.getString(29);
		result.plan_profile = struct.getString(30);
		result.joint_venture = struct.getString(31);
		result.recovery_ind = struct.getString(32);
		result.equity_type = struct.getString(33);
		result.jv_object_type = struct.getString(34);
		result.jv_jib_class = struct.getString(35);
		result.jv_jib_sub_class = struct.getString(36);
		result.objectclass_ext = struct.getString(37);
		result.func_area = struct.getString(38);
		result.func_area_long = struct.getString(39);

		return result;
	}
	
	private static PSPHierarchy setChildren(PSPHierarchy h) {
		PSPHierarchy next = h.down_link;
		
		while (next != null) {
			h.children.add(next);
			next = setChildren(next);
		}
		
		return h.right_link;
	}
}
